// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: builder.scala 47 2010-01-12 20:22:35Z normen.mueller $
package scaup.data

import java.text.MessageFormat.{format}

import scaup.Views._
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//sealed abstract class AbstractGraph
//
//case class Graph(id: String, attributes: Set[Attr], nodes: Set[Node], edges: Set[Edge]) extends AbstractGraph
//case class Node(id: String, attributes: Set[Attr])
//case class Edge(attributes: Set[Attr], left: String, right: String)
//case class Attr(name: String, value: String)
//
//import scaup.util.parsing._
//import scaup.util.parsing.dot.DOT.parse
//
///** 
// * @todo Distribute global node/ edge attributes
// * 
// * Note: Global 'graph' attributes within subgraphs are discarded.
// * 
// * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
// * @version 0.0.5 */
//object Graph {
//  def apply(input: String): Graph = parse(input) match {
//    case (dot.Graph(false, true, Some(id), ss@_*)) =>
//      ((ms: (Id2Attr, Id2Attr, Id2Edge)) => {
//        
//        ms._3 map {case (id, (as, ds)) => Edge(
//          Set.empty[Attr] ++ (as map {case (name, value) => Attr(name, value)}),
//          id,
//          null
//        )}
//        
//        Graph(id,
//              Set.empty[Attr] ++ (ms._1("graph") map {case (name, value) => Attr(name, value)}), 
//              Set.empty[Node] ++ (ms._2 map {case (id, as) => Node(id, Set.empty[Attr] ++ (as map {case (name, value) => Attr(name, value)}))}),
//              Set())   
//      })(slurp(ss.toList, Map().withDefaultValue(Map()), Map().withDefaultValue(Map()), Map().withDefaultValue((Map(), Set()))))      
//    case _ =>
//      throw new GraphError(format("DOT graph transformation error in ''{0}''", input))
//  } 
//  
//  type Attributes = Map[String, String]
//  type Dependents = Set[String]
//  type Id2Attr = Map[String, Attributes]
//  type Id2Edge = Map[String, (Attributes, Dependents)]
//  
//  def slurp(ns: List[dot.Statement], am: Id2Attr, nm: Id2Attr, em: Id2Edge): (Id2Attr, Id2Attr, Id2Edge) = ns match {
//    case Nil => (am, nm, em)
//    case dot.Attrs(kind, as@_*)::tl =>
//      slurp(tl, am + {(kind, am(kind) ++ (as map {case dot.Attr(key, value) => {(key, value)}}))}, nm, em)
//    case dot.Node(id, as@_*)::tl =>
//      slurp(tl, am, nm + {(id, nm(id) ++ (as map {case dot.Attr(key, value) => {(key, value)}}))}, em)
//    case dot.Edge(as, (l: dot.Node), (r: dot.Node))::tl =>      
//      ((nm: Id2Attr) =>
//        slurp(tl, am, nm, em + {(
//          l.id,
//          (((ev: (Attributes, Dependents)) =>
//            (
//              ev._1 ++ (as map {case dot.Attr(key, value) => {(key, value)}}),
//              ev._2 + r.id
//            )
//          )(em(l.id)))
//        )})
//      )((nm + {(l.id, nm(l.id))} + {(r.id, nm(r.id))}))
//    case hd::tl => slurp(tl, am, nm, em)
//  }
//}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//object Graph {
//  def apply(input: String): Graph = parse(input) match {
//    case (dot.Graph(false, true, Some(id), ss@_*)) =>
//      val (am, nm, em) = slurp(ss.toList, A(), Map(), Map())
//      
//      //val attributes = am.getOrElse("graph", dot.Attrs("graph", Set())).attributes
//      val attributes = am("graph").attributes
//      val nodes = Set.empty[Node] ++ (nm.values map {case dot.Node(id, attrs) => Node(id, attrs)})
//      val edges = Set.empty[Edge] ++ (em flatMap {
//        case (key, (as, ds)) => ((Nil: List[Edge]) /: (ds map (x =>
//          Edge(as map {case dot.Attr(name, value) => Attr(name, value)}, Node(key, nm(key).attributes), Node(x.id, nm(x.id).attributes))))
//        )(_ ::: List(_))
//      })
//      
//      Graph(id, attributes, nodes, edges)
//    case _ =>
//      throw new GraphError(format("DOT graph transformation error in ''{0}''", input))
//  } 
//  
//  type A = Map[String, Attr] with scala.collection.mutable.MultiMap[String, Attr]
//  type NodeMap = Map[String, dot.Node]
//  type EdgeMap = Map[String, Pair[Set[dot.Attr],Set[dot.Node]]]  
//  
//  def slurp(ns: List[dot.Statement], am: A, nm: NodeMap, em: EdgeMap): (A, NodeMap, EdgeMap) = ns match {
//    case Nil                                                 => (am, nm, em)
//    case (a@dot.Attrs(kind, as))::tl                         => slurp(tl, am |+| a, nm, em)
//    case (v@dot.Node(id, as))::tl                            => slurp(tl, am, add(nm, v), em)      
//    case dot.Edge(eas, (l: dot.Node), (r: dot.Node))::tl     =>
//      ((nm: NodeMap) => slurp(tl, am, nm, add(em, eas, l.id, nm(r.id))))(add(nm, List(l, r)))
//    case dot.Edge(eas, (l: dot.Node), (r: dot.Subgraph))::tl =>
//      val (_, snm, sem) = slurp(r.statements.toList, A(), Map(), Map())
//      ((nm: NodeMap) => slurp(tl, am, nm, add(em, eas, l.id, snm.values.toList) ++ sem))(add(nm, l::snm.values.toList))
//    case dot.Edge(eas, (l: dot.Subgraph), (r: dot.Node))::tl =>
//      val (_, snm, sem) = slurp(l.statements.toList, A(), Map(), Map())
//      ((nm: NodeMap) => slurp(tl, am, nm ++ snm, add(em, eas, snm.values.toList, nm(r.id)) ++ sem))(add(nm, r))            
////      val nnm = nm + {(r.id, dot.Node(r.id, nm.getOrElse(r.id, dot.Node(r.id, Set())).attributes))}
////      val nem = (em /: snm.values)((m,n) => {
////        (m.getOrElse(n.id, (Set.empty[dot.Attr],Set.empty[dot.Node]))) match {
////          case (as, ds) => m + {(n.id, ((as ++ eas), (ds + nnm(r.id))))}
////        }                             
////      })
////      slurp(tl, am, nnm ++ snm, nem)
//    case dot.Edge(eas, (l: dot.Subgraph), (r: dot.Subgraph))::tl => error("not yet implemented")
//    case dot.Subgraph(id,ss@_*)::tl =>
//      val (_, snm, sem) = slurp(ss.toList, A(), Map(), Map())
//      slurp(tl, am, nm ++ snm, em ++ sem)
//  }
//  
////  private def add(m: AttrMap, a: dot.Attrs) =
////    m + {(a.kind, dot.Attrs(a.kind, a.attributes ++ m.getOrElse(a.kind, dot.Attrs(a.kind, Set())).attributes))}
//  
//  private def add(m: NodeMap, v: dot.Node) = m + {(v.id, dot.Node(v.id, v.attributes ++ m.getOrElse(v.id, dot.Node(v.id, Set())).attributes))}
//  private def add(m: NodeMap, v: List[dot.Node]): NodeMap = v match {
//    case Nil    => m
//    case hd::tl => (add(m, hd) /: tl)(add(_,_))
//  }
////  private def add(x: NodeMap, y: NodeMap): NodeMap = x ++ y
//  
//  private def add(m: EdgeMap, nas: Set[dot.Attr], id: String, r: dot.Node) = m.getOrElse(id, (Set.empty[dot.Attr], Set.empty[dot.Node])) match {
//    case (as, ds) => m + {(id, ((as ++ nas), (ds + r)))}
//  }
//  private def add(m: EdgeMap, nas: Set[dot.Attr], id: String, r: List[dot.Node]): EdgeMap = r match {
//    case Nil    => m
//    case hd::tl => (add(m, nas, id, hd) /: tl)(add(_, nas, id, _))
//  } 
//  private def add(m: EdgeMap, nas: Set[dot.Attr], l: List[dot.Node], r: dot.Node): EdgeMap = m
////  private def add(x: EdgeMap, y: EdgeMap) = x ++ y
//  
//  private implicit def view(as: Set[dot.Attr]): Set[Attr] = as map {case dot.Attr(name, value) => Attr(name, value)}
//}
/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.5 */
case class GraphError(message: String) extends Exception(message)